{
module Language.Coal.Parser(parse, Expression(..), Statement(..), Function(..)) where
import qualified Language.Coal.Lexer as L
import Language.Coal.RawAST
import qualified Language.Coal.Type as T
}
%name parse

%tokentype { L.Token }

%error { parseError }

%token 
      while             { L.While }
      if              	{ L.If }
      int             	{ L.Integer $$}
      identificator	{ L.Identificator $$ }
      intType		{ L.IntType }
      boolType		{ L.BoolType }
      '='             	{ L.Asign }
      '+'             	{ L.Plus }
      '-'             	{ L.Minus }
      '*'             	{ L.Times }
      '/'             	{ L.Divide }
      '%'             	{ L.Rem }
      '('             	{ L.OpenParenth }
      ')'             	{ L.CloseParenth }
      ';'             	{ L.Semicolon }
      ':'		{ L.Colon }
      ','             	{ L.Coma }
      '=='		{ L.Equal}
      '!='		{ L.NotEqual}
      '<'		{ L.Less}
      '>'		{ L.Greater}
      '<='		{ L.LessEqual}
      '>='		{ L.GreaterEqual}
      '{'		{ L.OpenBrace}
      '}'		{ L.CloseBrace}
      newline          	{ L.Newline }
      then          	{ L.Then}
      do          	{ L.Do}
      end          	{ L.End}
      var        	{ L.Var}
      def        	{ L.Def}
      return		{ L.Return }

%%

Program :: {[Function]}
Program : Def {[$1]}
	| Def Term Program {$1 : $3}

Def :: { Function}
Def :  def identificator '(' ArgList')' ':' Type Term Statements end {(Function {name = $2, args = $4, body = $9, rtype = $7})} 
	| def identificator ':' Type  Term Statements end {(Function {name = $2, args = [], body = $6, rtype = $4})}

ArgList :: { [(String, T.Type)] } 
ArgList : identificator ':' Type {[($1, $3)]}
	| identificator ':' Type ',' ArgList {($1, $3) : $5}

Statements :: { [Statement] }
Statements : Statement { [$1]} |
	Statement Term { [$1]} |
	Statement Term Statements { $1 : $3 }

Statement :: { Statement }
Statement : Expression {Expression $1} |
	var identificator ':' Type {Declare $2 (Just $4)} |
	var identificator ':' Type '=' Expression {DeclareAndAsign $2 $6 (Just $4)} |
	var identificator {Declare $2 Nothing} |
	var identificator '=' Expression {DeclareAndAsign $2 $4 Nothing} |
	if Expression Then Statements end {If $2 $4} |
	while Expression Do Statements end {While $2 $4} |
	Expression if Expression {If $3 [Expression $1]} |
	Expression while Expression {While $3 [Expression $1]} |
	return Expression {Return $2}

Expression :: { Expression }
Expression : identificator '=' Expression {Asign $1 $3} |
	Expression1 {$1}

Expression1 :: { Expression }
Expression1 : Expression2 '==' Expression2 {Call "==" [$1, $3]} |
	Expression2 '!=' Expression2 {Call "!=" [$1, $3]} |
	Expression2 '<' Expression2 {Call "<" [$1, $3]} |
	Expression2 '>' Expression2 {Call ">" [$1, $3]} |
	Expression2 '<=' Expression2 {Call "<=" [$1, $3]} |
	Expression2 '>=' Expression2 {Call ">=" [$1, $3]} |
	Expression2 {$1}
	                                          
Expression2 :: { Expression }
Expression2 : Expression2 '+' Expression3 {Call "+" [$1, $3]} 
	| Expression2 '-' Expression3 {Call "-" [$1, $3]} 
	| Expression3 {$1}

Expression3 :: { Expression }
Expression3 : Expression3 '*' Expression4 {Call "*" [$1, $3]} 
	| Expression3 '/' Expression4 {Call "/" [$1, $3]} 
	| Expression3 '%' Expression4 {Call "%" [$1, $3]} 
	| Expression4 {$1}


Expression4 :: { Expression }
Expression4 : Primary {$1}
	| '(' Expression ')' {$2}

Primary :: { Expression }
Primary : int { Integer $1 } |
	identificator {Identificator $1}

Type :: { T.Type }
Type : intType { T.integer }
	| boolType { T.boolean }

Then : 	Term {} |
	then {} |
	Term then {}
Do : 	Term {} |
	do {} |
	Term do {}


Term : newline Term {}|
	';' Term {}|
	newline {} |
	';' {}


{

parseError :: [L.Token] -> a
parseError t = error ("Parse error " ++ (show t))

}